﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using SimpleFMMatrix.DataStructures;

namespace SimpleFMMatrix.ViewModel
{
    public class FMMatrixVM : DependencyObject
    {
        public FMMatrix FMMatrix { get; private set; }
        public OperatorVM[] Operators { get; private set; }
        public ValuedDependencyObject EnvelopeHZoom { get; private set; }
        public ModulationIndexVM[][] ModulationIndices { get; private set; }
        public OperatorOutputLevelVM[] OutputLevels { get; private set; }
        public PianoKeyVM[] PianoKeys { get; private set; }
        public ConvertMidiFileCommand ConvertMidiFileCommand { get; private set; }


        public Geometry PreviewCurve
        {
            get { return (Geometry)GetValue(PreviewCurveProperty); }
            set { SetValue(PreviewCurveProperty, value); }
        }

        public static readonly DependencyProperty PreviewCurveProperty =
            DependencyProperty.Register("PreviewCurve", typeof(Geometry), typeof(FMMatrixVM),
                new PropertyMetadata(null));



        public Geometry PreviewSpectrum
        {
            get { return (Geometry)GetValue(PreviewSpectrumProperty); }
            set { SetValue(PreviewSpectrumProperty, value); }
        }

        public static readonly DependencyProperty PreviewSpectrumProperty =
            DependencyProperty.Register("PreviewSpectrum", typeof(Geometry), typeof(FMMatrixVM),
                new PropertyMetadata(null));



        private void BuildPreview()
        {
            var pts = FMMatrix.PreviewWaveForm().Select((d, i) => new Point(i, (-d + 1) * 25));
            var cur = new StreamGeometry();

            using (var c = cur.Open())
            {
                c.BeginFigure(pts.First(), false, false);
                c.PolyLineTo(pts.ToList(), true, false);
            }

            cur.Freeze();
            PreviewCurve = cur;

            var scale = 50.0;
            var cptso = FMMatrix.PreviewSpectrum();
            var cpts = cptso.Select((d, i) => new Point(i, (-d + 1) * scale));
            var ccur = new StreamGeometry();

            using (var c = ccur.Open())
            {
                c.BeginFigure(new Point(0, scale), true, true);
                c.PolyLineTo(cpts.ToList(), true, false);
                c.LineTo(new Point(cptso.Length, scale), true, false);
            }

            ccur.Freeze();
            PreviewSpectrum = ccur;
        }

        internal FMMatrixVM(FMMatrix matrix)
        {
            FMMatrix = matrix;

            Operators = matrix.Operators
                .Select((o, i) => new OperatorVM(o, ((char)('A' + i)).ToString(), BuildPreview))
                .ToArray();

            EnvelopeHZoom = new ValuedDependencyObject(8, (o, n) =>
            {
                foreach (var op in Operators)
                    op.EnvelopeVM.HZoom.Value = n;
            });

            ModulationIndices = matrix.ModIndices
                .Select((o, i, j) => new ModulationIndexVM(matrix, i, j, BuildPreview));

            OutputLevels = matrix.OutputLevels
                .Select((o, i) => new OperatorOutputLevelVM(matrix, i, BuildPreview))
                .ToArray();

            PianoKeys = PianoKey.AllKeys
                .Select(k => new PianoKeyVM(matrix, k))
                .ToArray();

            ConvertMidiFileCommand = new ViewModel.ConvertMidiFileCommand(matrix);

            BuildPreview();
        }
    }
}
